package com.hexb.core.scanner

import com.hexb.core.annotation.Dict
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.core.io.Resource
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import org.springframework.core.io.support.ResourcePatternResolver
import org.springframework.core.type.classreading.CachingMetadataReaderFactory
import org.springframework.core.type.classreading.MetadataReader
import org.springframework.core.type.classreading.MetadataReaderFactory
import org.springframework.util.ClassUtils
import org.springframework.util.StringUtils
import org.springframework.util.SystemPropertyUtils

import java.lang.reflect.Method


/**
 * @author hexb
 * @create 2017-07-07 下午4:38
 * */

class DictionaryScanner {

    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"
    final List<Map<String, Object>> list = []
    Logger logger = LoggerFactory.getLogger(DictionaryScanner.class)
    private String scanPackages = ""

    DictionaryScanner(String scanPackages) {
        this.scanPackages = scanPackages
        scanner()
    }

    def scanner() {
        logger.info("\u001B[34m====>\u001B[0m begin to scan [valueEnums] in $scanPackages")
        String[] scanPackageArr = scanPackages.split(",")
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver()
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver)
        for (String basePackage : scanPackageArr) {
            if (StringUtils.isEmpty(basePackage)) {
                continue
            }
            logger.info("\u001B[34m====>\u001B[0m scanning [valueEnums] ${basePackage}")
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage)) + "/" + DEFAULT_RESOURCE_PATTERN
            try {
                Resource[] resources = resourcePatternResolver.getResources(packageSearchPath)
                for (Resource resource : resources) {
                    loadClassMethod(metadataReaderFactory, resource)
                }
            } catch (Exception e) {
                logger.error("ValueEnums scan error!", e)
            }

        }
        logger.info("\u001B[34m====>\u001B[0m scan [valueEnums] finished，has ${list.size()} items")
    }

    private void loadClassMethod(MetadataReaderFactory metadataReaderFactory, Resource resource) throws IOException {

        try {
            if (resource.isReadable()) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource)
                if (metadataReader != null) {
                    String[] types = metadataReader.annotationMetadata.annotationTypes
                    if (types.contains(Dict.class.getName())) {
                        Class<?> clazz = Class.forName(metadataReader.getClassMetadata().getClassName())
                        Dict dict = clazz.getAnnotation(Dict.class)
                        Map item = [
                                typeName   : clazz.simpleName,
                                name       : dict.value(),
                                description: dict.description(),
                        ]
                        item.items = []
                        def constants = clazz.enumConstants
                        def map
                        constants.each {
                            map = [:]
                            map << [name: clazz.getMethod("name").invoke(it)]
                            Method[] methods = it.class.declaredMethods
                            methods.each { method ->
                                if (method.getName() == "getValue") {
                                    map << [value: clazz.getMethod("getValue").invoke(it)]
                                }
                                if (method.getName() == "getDescription") {
                                    map << [description: clazz.getMethod("getDescription").invoke(it)]
                                }
                            }
                            item.items << map
                        }
                        list << item
                    }
                }
            }
        } catch (Exception e) {
            logger.error("ValueEnums scan error!", e)
        }
    }
}
